
#include "GenPic.h"

#define WORD uint16_t
#define DWORD uint32_t
#define LONG int32_t

#pragma pack(push, 1)
typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;
    LONG  biWidth;
    LONG  biHeight;
    WORD  biPlanes;
    WORD  biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG  biXPelsPerMeter;
    LONG  biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
} BITMAPINFOHEADER, *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

typedef struct tagBITMAPFILEHEADER {
    WORD  bfType;
    DWORD bfSize;
    WORD  bfReserved1;
    WORD  bfReserved2;
    DWORD bfOffBits;
} BITMAPFILEHEADER, *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
#pragma pack(pop)

static void savePic(unsigned char* buf,int linesize,int width,int height,char* name)
{
    FILE* f;

    f= fopen(name,"wb");
    fprintf(f,"P5\n%d %d\n%d\n",width,height,255);
    for(int i = 0;i < height;i++)
    {
        fwrite(buf + i * linesize,1,width,f);
    }

    fclose(f);
}

static void saveBMP(struct SwsContext*swsCtx,AVFrame* frame,int w,int h,char* name)
{
    int dataSize = w * h * 3;
    FILE* f = NULL;
    // 1.将YUV frame 转成BGR24 Frame
    AVFrame* frameBGR = av_frame_alloc();
    frameBGR->width = w;
    frameBGR->height =h;
    frameBGR->format = AV_PIX_FMT_BGR24;

    av_frame_get_buffer(frameBGR,0);
    sws_scale(swsCtx,(const uint8_t* const*)frame->data,
              frame->linesize,0,frame->height,frameBGR->data,frameBGR->linesize);

    // 2.构造
    BITMAPINFOHEADER infoHeader;
    infoHeader.biSize = sizeof(BITMAPINFOHEADER);
    infoHeader.biWidth = w;
    infoHeader.biHeight = h * (-1);
    infoHeader.biBitCount = 24;
    infoHeader.biCompression = 0;
    infoHeader.biSizeImage = 0;
    infoHeader.biClrImportant = 0;
    infoHeader.biClrUsed = 0;
    infoHeader.biXPelsPerMeter = 0;
    infoHeader.biYPelsPerMeter = 0;
    infoHeader.biPlanes =0;

    // 3.构造
    BITMAPFILEHEADER fileHeader;
    fileHeader.bfType = 0x4d42;
    fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dataSize;
    fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    // 4.写入文件
    f = fopen(name,"wb");

    fwrite(&fileHeader,sizeof(BITMAPFILEHEADER),1,f);
    fwrite(&infoHeader,sizeof(BITMAPINFOHEADER),1,f);
    fwrite(frameBGR->data[0],1,dataSize,f);

    // 5.释放
    fclose(f);
    av_freep(&frameBGR->data[0]);
    av_free(frameBGR);
}


static int decode(AVCodecContext* ctx,struct SwsContext* swsCtx,
                  AVFrame* frame,AVPacket* pkt,const char* fileName)
{
    char buf[1024];
    int ret = -1;
    ret = avcodec_send_packet(ctx,pkt);
    if(ret < 0)
    {
        av_log(NULL,AV_LOG_ERROR,"failed to send frame to decoder");
        goto _END;
    }

    while(ret >= 0)
    {
        ret = avcodec_receive_frame(ctx,frame);
        if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            return 0;
        }
        else if(ret < 0)
        {
            return -1;
        }
        snprintf(buf,sizeof(buf),"%s-%d.bmp",fileName,ctx->frame_num);

        //  savePic(frame->data[0],
        //          frame->linesize[0],
        //          frame->width,
        //          frame->height,
        //          buf);

        saveBMP(swsCtx,frame,frame->width,frame->height,buf);
        if(pkt)
        {
            av_packet_unref(pkt);
        }
    }
_END:
    return 0;
}

int genPic(const char* src,const char* dst)
{
    // 1.处理参数
    av_log_set_level(AV_LOG_DEBUG);
    int ret = -1;
    int index = -1;

    AVFormatContext* pFmtCtx = NULL;
    AVFormatContext* oFmtCtx = NULL;
    const AVOutputFormat* outFmt = NULL;
    AVStream* outStream = NULL;
    AVStream* inStream = NULL;
    AVPacket* pkt;

    const AVCodec* codec = NULL;
    AVCodecContext* ctx = NULL;
    AVFrame* frame = NULL;

    struct SwsContext* swsCtx = NULL;

    // 2.打开多媒体文件
    ret = avformat_open_input(&pFmtCtx,src,NULL,NULL);

    if(ret < 0)
    {
        av_log(NULL,AV_LOG_ERROR,"%s\n",av_err2str(ret));
        return -1;
    }

    // 3.从多媒体文件中找到视频流
    index = av_find_best_stream(pFmtCtx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
    if(index < 0)
    {
        av_log(pFmtCtx,AV_LOG_ERROR,"does not include audio stream\n");
        goto _ERROR;
    }

    inStream = pFmtCtx->streams[index];

    // 4.查找解码器
    codec = avcodec_find_decoder(inStream->codecpar->codec_id);
    if(!codec)
    {
        av_log(NULL,AV_LOG_ERROR,"don't find codec \n");
        goto _ERROR;
    }

    // 5.创建解码器上下文
    ctx = avcodec_alloc_context3(codec);
    if(!ctx)
    {
        av_log(NULL,AV_LOG_ERROR,"no memrory\n");
        goto _ERROR;
    }

    avcodec_parameters_to_context(ctx,inStream->codecpar);

    // 6.解码器和解码器上下文绑定
    ret = avcodec_open2(ctx,codec,NULL);
    if(ret < 0)
    {
        av_log(ctx,AV_LOG_ERROR,"don't open codec : %s \n",av_err2str(ret));
        goto _ERROR;
    }

    // 6.1获取sws上下文
    swsCtx =sws_getCachedContext(NULL,
                         ctx->width,
                         ctx->height,
                         AV_PIX_FMT_YUV420P,
                         ctx->width,
                         ctx->height,
                         AV_PIX_FMT_BGR24,
                         SWS_BICUBIC,NULL,NULL,NULL);

    // 7.创建AVFrame
    frame = av_frame_alloc();
    if(!frame)
    {
        av_log(NULL,AV_LOG_ERROR,"no memory\n");
        goto _ERROR;
    }

    // 8.创建Packet
    pkt = av_packet_alloc();
    if(!pkt)
    {
        av_log(NULL,AV_LOG_ERROR,"no memory\n");
        goto _ERROR;
    }

    // 9.从源多媒体文件中读到视频数据目的文件中
    while(av_read_frame(pFmtCtx,pkt) >= 0)
    {
        if(pkt->stream_index == index)
        {
            decode(ctx,swsCtx,frame,pkt,dst);
        }
    }
    decode(ctx,swsCtx,frame,NULL,dst);

// 10.资源释放
_ERROR:
    if(pFmtCtx)
    {
        avformat_close_input(&pFmtCtx);
        pFmtCtx = NULL;
    }

    if(ctx)
    {
        avcodec_free_context(&ctx);
        ctx  =NULL;
    }

    if(frame)
    {
        av_frame_free(&frame);
        frame =NULL;
    }

    if(pkt)
    {
        av_packet_free(&pkt);
        pkt = NULL;
    }


    return 0;
}


/*
#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>

#define WORD uint16_t
#define DWORD uint32_t
#define LONG int32_t

#pragma pack(push, 1)  // 确保结构体紧密打包。问题出现在这里啊！！！
typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;
    LONG  biWidth;
    LONG  biHeight;
    WORD  biPlanes;
    WORD  biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG  biXPelsPerMeter;
    LONG  biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
} BITMAPINFOHEADER;

typedef struct tagBITMAPFILEHEADER {
    WORD  bfType;
    DWORD bfSize;
    WORD  bfReserved1;
    WORD  bfReserved2;
    DWORD bfOffBits;
} BITMAPFILEHEADER;
#pragma pack(pop)

static void saveBMP(struct SwsContext* swsCtx, AVFrame* frame, int target_w, int target_h, const char* name)
{
    if (!frame || !frame->data[0]) {
        av_log(NULL, AV_LOG_ERROR, "Invalid frame data for %s\n", name);
        return;
    }

    FILE* f = NULL;
    AVFrame* frameBGR = NULL;

    // 1. 将YUV frame 转成BGR24 Frame
    frameBGR = av_frame_alloc();
    if (!frameBGR) {
        av_log(NULL, AV_LOG_ERROR, "Cannot allocate BGR frame\n");
        return;
    }

    frameBGR->width = target_w;
    frameBGR->height = target_h;
    frameBGR->format = AV_PIX_FMT_BGR24;

    if (av_frame_get_buffer(frameBGR, 0) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot allocate BGR frame buffer\n");
        goto cleanup;
    }

    // 执行缩放和格式转换
    if (sws_scale(swsCtx, (const uint8_t* const*)frame->data,
                  frame->linesize, 0, frame->height,
                  frameBGR->data, frameBGR->linesize) <= 0) {
        av_log(NULL, AV_LOG_ERROR, "SWS scale failed for %s\n", name);
        goto cleanup;
    }

    // 计算正确的数据大小（BMP要求每行4字节对齐）
    int stride = (target_w * 3 + 3) & ~3;  // 每行字节数，4字节对齐
    int imageSize = stride * target_h;     // 整个图像数据大小

    // 2. 设置BITMAPINFOHEADER
    BITMAPINFOHEADER infoHeader;
    memset(&infoHeader, 0, sizeof(infoHeader));
    infoHeader.biSize = sizeof(BITMAPINFOHEADER);
    infoHeader.biWidth = target_w;
    infoHeader.biHeight = -target_h;  // 负值表示从上到下的位图
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;       // 24位BGR
    infoHeader.biCompression = 0;     // BI_RGB
    infoHeader.biSizeImage = imageSize;
    // 其他字段保持为0

    // 3. 设置BITMAPFILEHEADER
    BITMAPFILEHEADER fileHeader;
    memset(&fileHeader, 0, sizeof(fileHeader));
    fileHeader.bfType = 0x4D42;  // "BM"
    fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + imageSize;
    fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    // 4. 写入文件
    f = fopen(name, "wb");
    if (!f) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open file: %s\n", name);
        goto cleanup;
    }

    // 写入文件头
    fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f);
    fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f);

    // 写入图像数据（处理4字节对齐）
    uint8_t* row_data = (uint8_t*)malloc(stride);
    if (!row_data) {
        av_log(NULL, AV_LOG_ERROR, "Cannot allocate row buffer\n");
        goto cleanup;
    }

    for (int y = 0; y < target_h; y++) {
        // 复制一行数据
        memcpy(row_data, frameBGR->data[0] + y * frameBGR->linesize[0], target_w * 3);
        // 填充对齐字节（如果需要）
        for (int x = target_w * 3; x < stride; x++) {
            row_data[x] = 0;
        }
        fwrite(row_data, 1, stride, f);
    }

    free(row_data);
    fclose(f);
    av_log(NULL, AV_LOG_INFO, "Successfully saved: %s (%dx%d)\n", name, target_w, target_h);

cleanup:
    if (frameBGR) {
        av_frame_free(&frameBGR);
    }
}

static int decode(AVCodecContext* ctx, struct SwsContext* swsCtx,
                  AVFrame* frame, AVPacket* pkt, const char* fileName, int* frameCount)
{
    char buf[1024];
    int ret = -1;

    // 发送数据包到解码器
    if (pkt && pkt->size > 0) {
        ret = avcodec_send_packet(ctx, pkt);
        if (ret < 0 && ret != AVERROR(EAGAIN)) {
            av_log(NULL, AV_LOG_ERROR, "Failed to send packet to decoder: %s\n", av_err2str(ret));
            return -1;
        }
    } else if (!pkt) {
        // 刷新解码器（传入NULL packet）
        ret = avcodec_send_packet(ctx, NULL);
    }

    // 循环接收所有可用的解码帧
    while (1) {
        ret = avcodec_receive_frame(ctx, frame);
        if (ret == AVERROR(EAGAIN)) {
            // 需要更多输入数据
            return 0;
        } else if (ret == AVERROR_EOF) {
            // 解码结束
            return 0;
        } else if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "Error receiving frame: %s\n", av_err2str(ret));
            return -1;
        }

        // 成功解码出一帧
        (*frameCount)++;
        av_log(NULL, AV_LOG_INFO, "Decoded frame %d, %dx%d\n",
               *frameCount, frame->width, frame->height);

        // 检查帧数据是否有效
        if (frame->data[0] && frame->linesize[0] > 0) {
            snprintf(buf, sizeof(buf), "%s-%d.bmp", fileName, *frameCount);
            saveBMP(swsCtx, frame, 640, 360, buf);
        } else {
            av_log(NULL, AV_LOG_WARNING, "Frame has invalid data, skipping save\n");
        }

        av_frame_unref(frame);
    }

    return 0;
}

int genPic(const char* src, const char* dst)
{
    // 1. 初始化变量
    av_log_set_level(AV_LOG_INFO);
    int ret = -1;
    int video_stream_index = -1;
    int frameCount = 0;

    AVFormatContext* fmt_ctx = NULL;
    AVCodecContext* codec_ctx = NULL;
    const AVCodec* codec = NULL;
    AVFrame* frame = NULL;
    AVPacket* pkt = NULL;
    struct SwsContext* sws_ctx = NULL;

    // 2. 打开多媒体文件
    ret = avformat_open_input(&fmt_ctx, src, NULL, NULL);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open input file: %s\n", av_err2str(ret));
        goto cleanup;
    }

    // 3. 获取流信息
    ret = avformat_find_stream_info(fmt_ctx, NULL);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find stream information: %s\n", av_err2str(ret));
        goto cleanup;
    }

    // 4. 查找视频流
    video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (video_stream_index < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find video stream\n");
        goto cleanup;
    }

    AVStream* video_stream = fmt_ctx->streams[video_stream_index];
    av_log(NULL, AV_LOG_INFO, "Found video stream: index=%d, codec=%s, %dx%d\n",
           video_stream_index,
           avcodec_get_name(video_stream->codecpar->codec_id),
           video_stream->codecpar->width,
           video_stream->codecpar->height);

    // 5. 查找解码器
    codec = avcodec_find_decoder(video_stream->codecpar->codec_id);
    if (!codec) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find decoder\n");
        goto cleanup;
    }

    // 6. 创建解码器上下文
    codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
        av_log(NULL, AV_LOG_ERROR, "Cannot allocate codec context\n");
        goto cleanup;
    }

    // 7. 复制参数到解码器上下文
    ret = avcodec_parameters_to_context(codec_ctx, video_stream->codecpar);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot copy codec parameters: %s\n", av_err2str(ret));
        goto cleanup;
    }

    // 8. 打开解码器
    ret = avcodec_open2(codec_ctx, codec, NULL);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open codec: %s\n", av_err2str(ret));
        goto cleanup;
    }

    // 9. 创建SWS上下文用于格式转换和缩放
    sws_ctx = sws_getContext(
        codec_ctx->width,
        codec_ctx->height,
        codec_ctx->pix_fmt,
        640, 360,  // 目标尺寸
        AV_PIX_FMT_BGR24,
        SWS_BILINEAR,  // 使用双线性插值
        NULL, NULL, NULL
        );

    if (!sws_ctx) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create scaling context\n");
        goto cleanup;
    }

    // 10. 分配帧和包
    frame = av_frame_alloc();
    if (!frame) {
        av_log(NULL, AV_LOG_ERROR, "Cannot allocate frame\n");
        goto cleanup;
    }

    pkt = av_packet_alloc();
    if (!pkt) {
        av_log(NULL, AV_LOG_ERROR, "Cannot allocate packet\n");
        goto cleanup;
    }

    // 11. 读取并解码视频帧
    av_log(NULL, AV_LOG_INFO, "Start decoding video frames...\n");

    while (av_read_frame(fmt_ctx, pkt) >= 0 && frameCount < 10) { // 限制最多10帧用于测试
        if (pkt->stream_index == video_stream_index) {
            decode(codec_ctx, sws_ctx, frame, pkt, dst, &frameCount);
        }
        av_packet_unref(pkt);
    }

    // 12. 刷新解码器（获取所有延迟帧）
    av_log(NULL, AV_LOG_INFO, "Flushing decoder...\n");
    decode(codec_ctx, sws_ctx, frame, NULL, dst, &frameCount);

    av_log(NULL, AV_LOG_INFO, "Decoding completed. Total frames: %d\n", frameCount);

cleanup:
    // 13. 清理资源
    if (sws_ctx) {
        sws_freeContext(sws_ctx);
    }
    if (pkt) {
        av_packet_free(&pkt);
    }
    if (frame) {
        av_frame_free(&frame);
    }
    if (codec_ctx) {
        avcodec_free_context(&codec_ctx);
    }
    if (fmt_ctx) {
        avformat_close_input(&fmt_ctx);
    }

    return (frameCount > 0) ? 0 : -1;
}
*/

